package gui.mainFrame.testCreator;

import common.Constants;
import gui.GUIController;
import gui.mainFrame.testCreator.parameterEditor.ParamTable;
import gui.mainFrame.testCreator.parameterEditor.ParamTableModel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.border.TitledBorder;
import javax.swing.text.StyledDocument;
import net.sf.jhighlighter.kits.JavaHighlightKit;
import net.sf.jhighlighter.ui.FScrollPane;
import testGenerator.Controller;
import testGenerator.testMethodModel.AutoJUnitMethod;
import testGenerator.testMethodModel.AvailableTestMethods;
import testGenerator.testMethodModel.TestMethod;
import testGenerator.testMethodModel.TestPath;

/**
 *
 * @author William Whitney
 */
public class TestCreator extends JPanel
{
    private static final long serialVersionUID = 1L;

    private Controller controller;
    private GUIController guiController;
    private JTextPane additionalConstraints;
    private JTextField testResult;
    private JComboBox chosenMethod = new JComboBox();
    private GenericTestPointEditor genericTestPointEditor;
    private static final Dimension k_btn_size = new Dimension(150, 25);
    private JTextField constructorInput;
    private JLabel semi = new JLabel(", ");

    /**
     * Default constructor.
     * @param controller
     * @param guiController
     */
    public TestCreator(Controller controller, GUIController guiController)
    {
        this.controller = controller;
        this.guiController = guiController;

        //Setup panel
        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

        //Add Title
        JLabel title = new JLabel("Test Editor");
        title.setFont(Constants.getHeadingFont());
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        topPanel.add(title);
        this.add(topPanel);

        //Add info and param editor
        this.add(getHeader());

        //Add Aditional Constrants box
        this.add(getBody());

        //Add Expected output
        this.add(getFooter());
        
        this.chosenMethod.addActionListener(getTestMethodChangedListener());
    }

    private JPanel getMethodInfo()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        //Set title
        TitledBorder boarder = new TitledBorder("Method Information");
        panel.setBorder(boarder);

        //Method name
        JLabel currMethod = new JLabel("Method: " + controller.getCurrMethod().getAsString());
        panel.add(currMethod, BorderLayout.NORTH);

        //Execution path
        JLabel executionPath = new JLabel("Execution Path: " + controller.getCurrExecutionPath());
        panel.add(executionPath, BorderLayout.CENTER);

        //Set prefered height
        panel.setMaximumSize(new Dimension(1000, 120));

        return panel;
    }

    private JPanel getHeader()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        panel.add(getMethodInfo());

        ParamTable paramTable = new ParamTable(guiController, new ParamTableModel(controller));
        panel.add(paramTable);

        return panel;
    }

    private JPanel getBody()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        if (isShowConstructor())
        {
            panel.add(getConstructorPanel());
        }

        if (isShowAdditionalConstraints())
        {
            panel.add(getAdditionalConstraintsPanel());
        }

        return panel;
    }

    private JPanel getConstructorPanel()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT));

        //Make title
        TitledBorder title = new TitledBorder("Constructor");
        panel.setBorder(title);

        //Input constructor
        JLabel constLabel = new JLabel("instance = ");
        panel.add(constLabel);

        TestPath currBranch = this.controller.getCurrTestPath();

        if (currBranch.constructor.equals(""))
        {
            currBranch.constructor = "new " + this.controller.getClassName() + "();";
        }

        constructorInput = new JTextField(currBranch.constructor);
        constructorInput.setColumns(40);
        panel.add(constructorInput);

        return panel;
    }

    private JPanel getAdditionalConstraintsPanel()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        //Make title
        TitledBorder title = new TitledBorder("Additional Constraints");
        panel.setBorder(title);

        //Help str
        JTextArea helpText = new JTextArea("Specify any additional object state constraints below. The \nfollowing code will run before the test verification points \nand return value are checked. \nEx instance.setSize(4);");
        helpText.setBackground(this.getBackground());

        panel.add(helpText, BorderLayout.NORTH);

        //Make Additional constrants input box
        JavaHighlightKit kit = new JavaHighlightKit();
        additionalConstraints = new JTextPane((StyledDocument) kit.createDefaultDocument());
        additionalConstraints.setText(controller.getAdditionalTestConstraints());
        FScrollPane spane = new FScrollPane(additionalConstraints);
        panel.add(spane, BorderLayout.CENTER);

        //Set size
        panel.setPreferredSize(new Dimension(0, 200));

        return panel;
    }

    private JScrollPane getFooter()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        //Add return value
        if (isShowReturnValue())
        {
            panel.add(getReturnValuePanel(), BorderLayout.NORTH);
        }

        //Check to see if static method
        if (isShowAdditonalTestPoints())
        {

            //Make title for generic test points
            TitledBorder title = new TitledBorder("Test Verification Points");
            panel.setBorder(title);

            //Add generic test point buttons
            panel.add(getGenericTestPointsButtons(), BorderLayout.EAST);

            //Add generic test points
            genericTestPointEditor = new GenericTestPointEditor(controller);
            panel.add(genericTestPointEditor, BorderLayout.CENTER);
        }
        
        JScrollPane spane = new JScrollPane(panel);
        spane.setPreferredSize(new Dimension(200, 200));
        
        return spane;
    }

    private JPanel getGenericTestPointsButtons()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        JPanel innerPanel = new JPanel();
        innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));

        //Add Test Point btn
        JButton addBtn = new JButton("Add Test Point");

        addBtn.setMinimumSize(k_btn_size);
        addBtn.setMaximumSize(k_btn_size);
        addBtn.setPreferredSize(k_btn_size);
        addBtn.addActionListener(getAddBtnListener());
        innerPanel.add(addBtn);

        //Remove test point btn
        JButton removeBtn = new JButton("Remove Test Point");

        removeBtn.setMinimumSize(k_btn_size);
        removeBtn.setMaximumSize(k_btn_size);
        removeBtn.setPreferredSize(k_btn_size);
        removeBtn.addActionListener(getEditBtnListener());
        innerPanel.add(removeBtn);


        panel.add(innerPanel, BorderLayout.SOUTH);
        return panel;
    }
    
    private ActionListener getTestMethodChangedListener()
    {
        return new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e) 
            {
                if ( ((TestMethod)chosenMethod.getSelectedItem()).isAllowsTwoArgs() )
                {
                    testResult.setVisible(true);
                    semi.setText(", ");
                }
                else 
                {
                    testResult.setVisible(false);
                    semi.setText("");
                }
            }
            
        };
    }

    private ActionListener getAddBtnListener()
    {
        return new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                genericTestPointEditor.addTestPoint();
            }
        };
    }

    private ActionListener getEditBtnListener()
    {
        return new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                genericTestPointEditor.deleteTestPoint();
            }
        };
    }

    private JPanel getReturnValuePanel()
    {
        //Add return value
        JPanel returnValuePanel = new JPanel();
        //Set layout
        FlowLayout layout = new FlowLayout(FlowLayout.LEFT);
        returnValuePanel.setLayout(layout);
        
        JLabel returnValue = new JLabel("Return value: ");
        returnValuePanel.add(returnValue);

        chosenMethod = new JComboBox(AvailableTestMethods.getAvailableMethods().toArray());
        returnValuePanel.add(chosenMethod);
        
        JLabel returnValueBracket = new JLabel("(");
        returnValuePanel.add(returnValueBracket);

        testResult = new JTextField(12);
        testResult.setText(controller.getBranchExpectedReturnValue());
        returnValuePanel.add(testResult);

        String calle;
        if (controller.getCurrMethod().hasModifier("static"))
        {
            calle = controller.getClassName() + ".";
        }
        else
        {
            calle = "instance.";
        }
        
        
        returnValuePanel.add(semi);
        JLabel semiColon = new JLabel(calle + this.controller.getCurrMethod().getAsStringSimple() + " );");
        returnValuePanel.add(semiColon);

        return returnValuePanel;

    }

    public void saveTest()
    {
        if (isShowAdditionalConstraints())
        {
            this.controller.setAdditionalTestConstraints(additionalConstraints.getText());
        }
        if (isShowReturnValue())
        {
            this.controller.setBranchExpectedReturnValue(testResult.getText());
            this.controller.setBranchTestMethod((TestMethod) chosenMethod.getSelectedItem());
        }

        if (isShowConstructor())
        {
            this.controller.setTestConstructor(constructorInput.getText());
        }

        if (isShowAdditonalTestPoints())
        {
            genericTestPointEditor.saveTestPoints();
        }
        else
        {
            this.controller.getGenericTestPoints().clear();
        }
    }

    private boolean isShowConstructor()
    {
        AutoJUnitMethod currMethod = this.controller.getCurrMethod();
        return !currMethod.hasModifier("static") && !currMethod.isConstructor();
    }

    private boolean isShowReturnValue()
    {
        AutoJUnitMethod currMethod = this.controller.getCurrMethod();
        return !currMethod.isVoidMethod() && !currMethod.isConstructor();
    }

    private boolean isShowAdditonalTestPoints()
    {
        return !this.controller.getCurrMethod().hasModifier("static");
    }

    private boolean isShowAdditionalConstraints()
    {
        AutoJUnitMethod currMethod = this.controller.getCurrMethod();
        return !currMethod.hasModifier("static");
    }
}
